Build a Weather Station with Elixir and Nerves by Alexander Koutmos & Bruce Tate & Frank Hunleth
Author:Alexander Koutmos & Bruce Tate & Frank Hunleth [Alexander Koutmos]
Language: eng
Format: epub
Publisher: Pragmatic Bookshelf
Published: 2022-01-05T16:00:00+00:00
Wrap the Core and Hardware in a GenServer
Itâs customary to wrap sensors in OTP GenServers because there are often requirements to read from them periodically to get consistent readings. In our case, the wrapping is also practical because the code must wait between readings. The amount of time is specified in the sensorâs spec sheet. By periodically taking measurements every second, weâll be fine. Letâs build a GenServer to do so.
Since the GenServer will also double as the API for the whole project, we can put the GenServer in the top-level lib/veml6030.ex file. Go ahead and delete the module code created by mix new and start with the GenServer boilerplate:
â âdefmoduleâ VEML6030 âdoâ
â âuseâ GenServer
â
â ârequireâ Logger
â
â alias VEML6030.{Comm, Config}
â âendâ
Here we use, require, and alias various dependencies as needed. Next, add two versions of the init/1 GenServer callback, like so:
â âdefmoduleâ VEML6030 âdoâ
â ...
â
â @impl true
â âdefâ init(%{âaddress:â address, âi2c_bus_name:â bus_name} = args) âdoâ
â i2c = Comm.open(bus_name)
â
â config =
â args
â |> Map.take([â:gainâ, â:int_timeâ, â:shutdownâ, â:interruptâ])
â |> Config.new()
â
â Comm.write_config(config, i2c, address)
â â:timerâ.send_interval(1_000, â:measureâ)
â
â state = %{
â âi2c:â i2c,
â âaddress:â address,
â âconfig:â config,
â âlast_reading:â â:no_readingâ
â }
â
â {â:okâ, state}
â âendâ
â âendâ
This version creates a configuration and opens the I2C bus. It also sends a periodic message to itself at a one-second interval. Then it sets the state of the GenServer.
Now we can write an alternative init to discover the address and bus if theyâre not set, like this:
â âdefmoduleâ VEML6030 âdoâ
â ...
â
â âdefâ init(args) âdoâ
â {bus_name, address} = Comm.discover()
â transport = â"ââbus: ââ#{âbus_nameâ}ââ, address: ââ#{âaddressâ}ââ"â
â
â Logger.info(â"ââStarting VEML6030. Please specify an address and a bus."â)
â Logger.info(â"ââStarting on "â <> transport)
â
â defaults =
â args
â |> Map.put(â:addressâ, address)
â |> Map.put(â:i2c_bus_nameâ, bus_name)
â
â init(defaults)
â âendâ
â âendâ
If either the address or bus is not there, the code automatically discovers the sensor, creates a set of default configurations, and calls our prior init/1 implementation. Next, weâll write the GenServer callbacks to handle our :measure message thatâs sent at a regular one-second interval and the :get_measurement message that returns the GenServerâs last measurement reading:
â âdefmoduleâ VEML6030 âdoâ
â ...
â
â @impl true
â âdefâ handle_info(
â â:measureâ,
â %{âi2c:â i2c, âaddress:â address, âconfig:â config} = state
â ) âdoâ
â last_reading = Comm.read(i2c, address, config)
â updated_with_reading = %{state | âlast_reading:â last_reading}
â
â {â:noreplyâ, updated_with_reading}
â âendâ
â
â @impl true
â âdefâ handle_call(â:get_measurementâ, _from, state) âdoâ
â {â:replyâ, state.last_reading, state}
â âendâ
â âendâ
The handle_info/2 callback, which handles the :measure message, updates the :last_reading key in our GenServer state based on the response from our Comm module. This updated GenServer state now contains the latest light measurement from the VEML6030 sensor.
To make this data easily accessible, weâll also want to add a couple of public API functions:
â âdefmoduleâ VEML6030 âdoâ
â ...
â
â âdefâ start_link(options \ %{}) âdoâ
â GenServer.start_link(__MODULE__, options, âname:â __MODULE__)
â âendâ
â
â âdefâ get_measurement âdoâ
â GenServer.call(__MODULE__, â:get_measurementâ)
â âendâ
â
â ...
â âendâ
Our start_link/1 function wraps the GenServer start_link/3 function and sets the name of the GenServer process to VEML6030 (the name of the module). The get_measurement/0 function is another GenServer wrapper that makes it easier to fetch the light measurement from the GenServer process. Since we gave our
Download
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.
Ajax | Assembly Language Programming |
Borland Delphi | C & C++ |
C# | CSS |
Compiler Design | Compilers |
DHTML | Debugging |
Delphi | Fortran |
Java | Lisp |
Perl | Prolog |
Python | RPG |
Ruby | Swift |
Visual Basic | XHTML |
XML | XSL |
Deep Learning with Python by François Chollet(12437)
Hello! Python by Anthony Briggs(9784)
OCA Java SE 8 Programmer I Certification Guide by Mala Gupta(9676)
The Mikado Method by Ola Ellnestam Daniel Brolund(9669)
Dependency Injection in .NET by Mark Seemann(9211)
Algorithms of the Intelligent Web by Haralambos Marmanis;Dmitry Babenko(8178)
Test-Driven iOS Development with Swift 4 by Dominik Hauser(7670)
Grails in Action by Glen Smith Peter Ledbrook(7591)
The Well-Grounded Java Developer by Benjamin J. Evans Martijn Verburg(7431)
Becoming a Dynamics 365 Finance and Supply Chain Solution Architect by Brent Dawson(6557)
Microservices with Go by Alexander Shuiskov(6314)
Secrets of the JavaScript Ninja by John Resig Bear Bibeault(6302)
Practical Design Patterns for Java Developers by Miroslav Wengner(6226)
Test Automation Engineering Handbook by Manikandan Sambamurthy(6200)
Angular Projects - Third Edition by Aristeidis Bampakos(5543)
The Art of Crafting User Stories by The Art of Crafting User Stories(5127)
NetSuite for Consultants - Second Edition by Peter Ries(5080)
Kotlin in Action by Dmitry Jemerov(4941)
Demystifying Cryptography with OpenSSL 3.0 by Alexei Khlebnikov(4877)
